#include "gamepad.h"

#include <SDL2/SDL.h>

GamePadManager::GamePadManager(bool enableLog) : doLog(enableLog) {
    SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
}

GamePadManager::~GamePadManager() {
    for (auto &p: gamepads)
        SDL_GameControllerClose(p.second);
    gamepads.clear();
    SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
}

void GamePadManager::AddPlayStationClassicControllerSupport() {
#if defined(__linux__)
    SDL_GameControllerAddMapping("030000004c050000da0c000011010000,Sony Interactive Entertainment Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftx:a0,lefty:a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,");
#endif
#if defined(_WIN32)
    SDL_GameControllerAddMapping("030000004c050000da0c000000000000,Controller,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,");
#endif
}

bool GamePadManager::processEvents(SDL_Event &e) {
    switch (e.type) {
        case SDL_CONTROLLERDEVICEADDED: {
            if (doLog)
                fprintf(stdout, "Gamepad added: %02X", e.cdevice.which);
            auto *gamepad = SDL_GameControllerOpen(e.cdevice.which);
            if (gamepad) {
                auto id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad));
                gamepads[id] = gamepad;
                buttons[id] = {false};
                axes[id] = {0.f};
            }
            return gamepad != nullptr;
        }
        case SDL_CONTROLLERDEVICEREMOVED: {
            if (doLog)
                fprintf(stdout, "Gamepad removed: %02X", e.cdevice.which);
            auto ite = gamepads.find(e.cdevice.which);
            if (ite == gamepads.end()) return false;
            buttons.erase(e.cdevice.which);
            axes.erase(e.cdevice.which);
            SDL_GameControllerClose(ite->second);
            gamepads.erase(ite);
            return true;
        }
        case SDL_CONTROLLERBUTTONDOWN: {
            if (e.cbutton.button > 14) return false;
            if (doLog)
                fprintf(stdout, "Gamepad pressed: %02X %02X", e.cbutton.which, e.cbutton.button);
            buttons[e.cbutton.which][e.cbutton.button] = true;
            onButton(e.cbutton.which, e.cbutton.button, true);
            return true;
        }
        case SDL_CONTROLLERBUTTONUP: {
            if (e.cbutton.button > 14) return false;
            if (doLog)
                fprintf(stdout, "Gamepad released: %02X %02X", e.cbutton.which, e.cbutton.button);
            buttons[e.cbutton.which][e.cbutton.button] = false;
            onButton(e.cbutton.which, e.cbutton.button, false);
            return true;
        }
        case SDL_CONTROLLERAXISMOTION: {
            if (e.caxis.axis > 5) return false;
            if (doLog)
                fprintf(stdout, "Gamepad axis: %02X %02X %d", e.caxis.which, e.caxis.axis, e.caxis.value);
            float v;
            if (e.caxis.value > 12000) {
                v = (static_cast<float>(e.caxis.value) - 12000.f) / (32767.f - 12000.f);
                axes[e.caxis.which][e.caxis.axis] = v;
            } else if (e.caxis.value >= -12000) {
                v = 0.f;
                axes[e.caxis.which][e.caxis.axis] = 0.f;
            } else {
                v = (-12000.f - static_cast<float>(e.caxis.value)) / (-32768.f - -12000.f);
                axes[e.caxis.which][e.caxis.axis] = v;
            }
            onAxis(e.caxis.which, e.caxis.axis, v);
            return true;
        }
        default:
            break;
    }
    return false;
}
